package com.gitee.huanminabc.utils_common.obj;

import com.gitee.huanminabc.jcommon.exception.CommonException;
import com.gitee.huanminabc.jcommon.file.FileReadUtil;
import com.gitee.huanminabc.jcommon.multithreading.executor.ExecutorUtil;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.TokenRange;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.NormalAnnotationExpr;
import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr;
import com.github.javaparser.ast.nodeTypes.NodeWithName;
import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName;
import com.github.javaparser.ast.nodeTypes.NodeWithTokenRange;
import com.github.javaparser.ast.type.ArrayType;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.helper.StringUtil;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

//解析java代码文件(纯文本)
@Slf4j
public class JavaParserUtil {
    //常用的集合类的名称,一般字段类型都是父类
    private static final Map<String, String> collectionMap = Maps.newHashMap();
    private static final Map<String, String> dates = Maps.newHashMap();
    private static final Map<String, String> primitiveWrapperMap = new HashMap<>();

    static {
        primitiveWrapperMap.put("Boolean", "java.lang.Boolean");
        primitiveWrapperMap.put("Byte", "java.lang.Byte");
        primitiveWrapperMap.put("Character", "java.lang.Character");
        primitiveWrapperMap.put("Short", "java.lang.Short");
        primitiveWrapperMap.put("Integer", "java.lang.Integer");
        primitiveWrapperMap.put("Long", "java.lang.Long");
        primitiveWrapperMap.put("Double", "java.lang.Double");
        primitiveWrapperMap.put("Float", "java.lang.Float");
        primitiveWrapperMap.put("String", "java.lang.String");
        primitiveWrapperMap.put("BigDecimal", "java.math.BigDecimal");

        dates.put("Date", "java.util.Date");
        dates.put("LocalDate", "java.time.LocalDate");
        dates.put("LocalDateTime", "java.time.LocalDateTime");
        dates.put("LocalTime", "java.time.LocalTime");

        ExecutorUtil.newThread(() -> {
            Map<String, String> subClassByInterface = ScanClassUtil.getSubClassByInterface(Map.class, Collection.class);
            collectionMap.putAll(subClassByInterface);
            collectionMap.put("List", "java.util.List");
            collectionMap.put("Map", "java.util.Map");
            collectionMap.put("Set", "java.util.Set");
            collectionMap.put("Queue", "java.util.Queue");
            collectionMap.put("Deque", "java.util.Deque");
            collectionMap.put("SortedSet", "java.util.SortedSet");
            collectionMap.put("SortedMap", "java.util.SortedMap");
        });
    }


    //文件路径解析
    public static Class parseJavaFile(String filePath) {
        //判断文件是否存在
        File file = new File(filePath);
        if (!file.exists()) {
            throw new CommonException("文件不存在");
        }
        String s = FileReadUtil.readAllStr(file);
        return parseJavaCode(s);
    }

    //解析文本java代码,返回类的各种信息
    public static Class parseJavaCode(String code) {
        //解析文本java代码,返回类的各种信息
        CompilationUnit parse = StaticJavaParser.parse(code);
        Class cls = new Class();
        TypeDeclaration<?> type1 = parse.getType(0);
        if (type1 instanceof ClassOrInterfaceDeclaration) {
            ClassOrInterfaceDeclaration type = (ClassOrInterfaceDeclaration) type1;
            cls.imports = parse.getImports().stream().map(NodeWithTokenRange::getTokenRange).filter(Optional::isPresent).map(Optional::get).map(TokenRange::toString).map((importStr)->{
                //去掉开头的import,去掉结尾的;
                return importStr.replaceAll("import|;","").trim();
            }).toArray(String[]::new);
            cls.name = type.getNameAsString();
            cls.modifier = type.getModifiers().get(0).getKeyword().asString();
            cls.interfaces = type.getImplementedTypes().stream().map(NodeWithSimpleName::getNameAsString).toArray(String[]::new);
            cls.annotations = type.getAnnotations().stream().map(JavaParserUtil::getAnnotation).toArray(Annotation[]::new);
            cls.packagePath = parse.getPackageDeclaration().map(NodeWithName::getNameAsString).orElse("");
            setFields(cls, type);
            setMethods(cls, type);

        } else if (type1 instanceof EnumDeclaration) {
            EnumDeclaration type = (EnumDeclaration) type1;
            cls.name = type.getNameAsString();
            cls.isEnum = type1.isEnumDeclaration();
            cls.modifier = type.getModifiers().get(0).getKeyword().asString();
            //获取注释
            cls.comment = getComment(type.getComment().map(Node::toString).orElse(""));
        }
        //获取注释
        cls.comment = getComment(type1.getComment().map(Node::toString).orElse(""));
        return cls;
    }

    private static void setMethods(Class cls, ClassOrInterfaceDeclaration type) {
        cls.methods = type.getMethods().stream().map(methodDeclaration -> {
            Method method = new Method();
            method.belongClass=cls;
            method.name = methodDeclaration.getNameAsString();
            method.returnType = getType(methodDeclaration.getType());
            NodeList<Modifier> modifiers = methodDeclaration.getModifiers();
            if (modifiers.isEmpty()) {
                method.modifier = "default";
            } else{
                method.modifier = modifiers.get(0).getKeyword().asString();
            }
            method.body = methodDeclaration.getBody().map(Node::toString).orElse("");
            method.parameters = methodDeclaration.getParameters().stream().map(parameter -> {
                Parameter p = new Parameter();
                p.name = parameter.getNameAsString();
                p.type = getType(parameter.getType());
                p.annotations = parameter.getAnnotations().stream().map(JavaParserUtil::getAnnotation).toArray(Annotation[]::new);
                return p;
            }).toArray(Parameter[]::new);
            method.exceptions = methodDeclaration.getThrownExceptions().stream().map((referenceType) -> {
                if (referenceType.isClassOrInterfaceType()) {
                    return referenceType.asClassOrInterfaceType().getNameAsString();
                }
                return "";
            }).toArray(String[]::new);

            method.annotations = methodDeclaration.getAnnotations().stream().map(JavaParserUtil::getAnnotation).toArray(Annotation[]::new);
            //获取方法的注释
            method.comment = getComment( methodDeclaration.getComment().map(Node::toString).orElse(""));

            return method;
        }).toArray(Method[]::new);
    }

    //注释处理
    private static String getComment(String comment) {
        comment = comment.replaceAll("/\\*\\*|\\*/|\\*|//", "");
        if (!StringUtil.isBlank(comment)) {
            //然后在清理空行
            String[] split = comment.split("\n");
            comment = String.join("\n", Arrays.stream(split).filter(s -> !StringUtil.isBlank(s)).map(String::trim).toArray(String[]::new));
        }
        return comment;
    }

    private static Type getType(com.github.javaparser.ast.type.Type typeReal) {
        Type type1 = new Type();
        type1.name = typeReal.asString();
        type1.isReference = typeReal.isReferenceType();
        type1.isPrimitive = typeReal.isPrimitiveType();
        type1.isVoid = typeReal.isVoidType();
        if (typeReal instanceof ClassOrInterfaceType) {//引用类型包括集合泛型但是不包括数组
            ClassOrInterfaceType classOrInterfaceType = typeReal.asClassOrInterfaceType();
            type1.name = classOrInterfaceType.getNameAsString();
            type1.fullName = classOrInterfaceType.asString();
            type1.isGeneric = classOrInterfaceType.getTypeArguments().isPresent();
            type1.generics = classOrInterfaceType.getTypeArguments().map(typeArguments -> typeArguments.stream().map(Node::toString).toArray(String[]::new)).orElse(new String[0]);
            //引用类型需要识别是否是8大基本类型+String
            type1.isPrimitive = isPrimitive(type1.name);
            //判断是否是集合
            type1.isCollection = isCollection(type1.name);
            //判断是否是时间类型
            type1.isDate = isDate(type1.name);

        } else if (typeReal instanceof ArrayType) {
            ArrayType arrayType = typeReal.asArrayType();
            type1.name = arrayType.getComponentType().asString();
            type1.fullName = arrayType.asString();
            type1.isArray = true;
        }
        //不是数组不是集合不是基本类型(包括包装类和String),不是void,就是存粹的定义对象
        type1.isReferenceProto = !type1.isCollection && !type1.isPrimitive && !type1.isVoid && !type1.isArray;
        return type1;
    }


    private static void setFields(Class cls, ClassOrInterfaceDeclaration type) {
        cls.fields = type.getFields().stream().map(fieldDeclaration -> {
            Field field = new Field();
            field.belongClass=cls;
            field.name = fieldDeclaration.getVariable(0).getNameAsString();
            field.type = getType(fieldDeclaration.getVariable(0));
            field.isStatic = fieldDeclaration.isStatic();
            field.isFinal = fieldDeclaration.isFinal();
            field.isTransient = fieldDeclaration.isTransient();
            field.isVolatile = fieldDeclaration.isVolatile();
            NodeList<Modifier> modifiers = fieldDeclaration.getModifiers();
            if (modifiers.isEmpty()) {
                field.modifier = "default";
            }else{
                field.modifier = modifiers.get(0).getKeyword().asString();
            }
            field.annotations = fieldDeclaration.getAnnotations().stream().map(JavaParserUtil::getAnnotation).toArray(Annotation[]::new);
            //添加注释, 去掉开头的/**  * 和结尾的*/  和//
            // 同一时间拥有 /** 和//  而//优先级大于/**  只会出现一种
            field.comment = getComment(fieldDeclaration.getComment().map(Node::toString).orElse(""));

            return field;
        }).toArray(Field[]::new);
    }

    private static Type getType(VariableDeclarator variableDeclarator) {
        Type type = new Type();
        type.name = variableDeclarator.getName().asString();
        com.github.javaparser.ast.type.Type typeReal = variableDeclarator.getType();
        return getType(typeReal);
    }

    /**
     * 一共就三种注解
     * MarkerAnnotationExpr @Override
     * SingleMemberAnnotationExpr @SuppressWarnings("unchecked")
     * NormalAnnotationExpr @RequestMapping(value = "/list", method = RequestMethod.GET)
     *
     * @param annotationExpr
     * @return
     */
    private static Annotation getAnnotation(AnnotationExpr annotationExpr) {
        Annotation annotation = new Annotation();
        annotation.setName(annotationExpr.getNameAsString());
        if (annotationExpr instanceof NormalAnnotationExpr) {
            NormalAnnotationExpr normalAnnotationExpr = (NormalAnnotationExpr) annotationExpr;
            annotation.attributes = normalAnnotationExpr.getPairs().stream().collect(
                    Collectors.toMap(pair -> pair.getName().asString(), pair -> pair.getValue().toString()));
        } else if (annotationExpr instanceof SingleMemberAnnotationExpr) {
            SingleMemberAnnotationExpr singleMemberAnnotationExpr = (SingleMemberAnnotationExpr) annotationExpr;
            HashMap<String, String> objectObjectHashMap = new HashMap<>();
            Expression memberValue = singleMemberAnnotationExpr.getMemberValue();
            objectObjectHashMap.put("value", memberValue.toString());
            annotation.attributes = objectObjectHashMap;
        }
        return annotation;
    }

    //类型
    @Data
    public static class Type {
        private String name; //这个是类型的名称

        private String fullName;//这个是类型的名称但是和name不一样,这个是全String[] List<String>  但是name就是原始名称String
        //是否是泛型
        private boolean isGeneric = false;
        //是否是数组
        private boolean isArray = false;
        //是否是集合
        private boolean isCollection = false;
        //是否是引用类型, 包含 数组 集合 包装类型 一般对象 只要有引用的就是true
        private boolean isReference = false;
        //是否是基本类型包括包装类和String
        private boolean isPrimitive = false;
        //判断是void
        private boolean isVoid = false;
        //判断不是数组不是集合不是基本类型(包括包装类和String),不是void,就是存粹的定义对象
        private boolean isReferenceProto = false;
        //是时间类型
        private boolean isDate = false;

        //泛型
        private String[] generics = new String[0];
    }

    //注解
    @Data
    public static class Annotation {
        private String name;
        private Map<String, String> attributes = new HashMap<>();

        //获取字符串没有""的 , 如果直接从attributes取出来的是有""的
        public String getStrValue(String key) {
            String str = attributes.get(key);
            if (str == null) {
                return "";
            }
            return delStrQuotationMark(str);
        }

        //获取布尔
        public boolean getBooleanValue(String key) {
            String str = attributes.get(key);
            if (str == null) {
                return false;
            }
            //必须是true或者false
            if (!"true".equals(str) && !"false".equals(str)) {
                throw new CommonException("不是布尔类型");
            }
            return Boolean.parseBoolean(str);
        }
    }

    @Data
    public static class Parameter {
        private String name;
        private Type type;
        private Annotation[] annotations = new Annotation[0];
    }

    @Data
    @ToString(exclude = {"belongClass"})
    public static class Method {
        private String name; //方法名
        private Type returnType; //返回类型
        private String modifier; //修饰符
        private String body; //方法体  ,这个就是纯文本不解析具体的语法
        private Parameter[] parameters = new Parameter[0]; //参数
        private String[] exceptions = new String[0]; //异常
        private Annotation[] annotations = new Annotation[0]; //注解

        //这个方法所属的类
        private  Class belongClass;

        //注释
        private String comment;
    }

    @Data
    @ToString(exclude = {"belongClass"})
    public static class Field {
        private String name;
        private Type type;
        //是否静态
        private boolean isStatic = false;
        //是否常亮
        private boolean isFinal = false;

        //是Transient
        private boolean isTransient = false;
        //是Volatile
        private boolean isVolatile = false;
        //修饰符
        private String modifier;
        //注释
        private String comment;
        private Annotation[] annotations = new Annotation[0];

        //这个字段所属的类
        private  Class belongClass;
    }

    @Data
    public static class Class {
        private String name;
        private String modifier;
        private String[] interfaces = new String[0];
        private Field[] fields = new Field[0];
        private Method[] methods = new Method[0];
        private Annotation[] annotations = new Annotation[0];
        private String[] imports = new String[0];
        //xx.xx.xx
        private String packagePath;
        //是枚举
        private boolean isEnum = false;
        //注释
        private String comment;
    }

    //如果字符有引号，就去掉
    public static String delStrQuotationMark(String str) {
        //判断开头和结尾是否有引号
        if (str.startsWith("\"") && str.endsWith("\"")) {
            str = str.substring(1, str.length() - 1);
        }
        return str;
    }


    public static String getPrimitivePackage(String name) {
        return primitiveWrapperMap.get(name);
    }
    public static boolean isPrimitive(String name) {
        return primitiveWrapperMap.containsKey(name);
    }


    public static boolean isDate(String name) {
        return dates.containsKey(name);
    }
    public static String getDatePackage(String name) {
        return dates.get(name);
    }


    //是否是集合
    public static boolean isCollection(String name) {
        waitInit();
        return collectionMap.containsKey(name);
    }
    public static String getCollectionPackage(String name) {
        waitInit();
        return collectionMap.get(name);
    }



    //如果collectionMap为空,就是还没有初始化完成,等待初始化完成
    private static void waitInit() {
        while (collectionMap.isEmpty()) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
